import sys

from PyQt5 import QtCore, QtGui
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtCore import Qt

'''
定制窗口基类
@Author: 浅若清风cyf
@Date: 2023/06/16
@Description: 支持窗口缩放，标题栏双击最大最小化，解决了依赖mouseMoveEvent识别鼠标位置导致鼠标样式更新不及时的问题
'''


class MainWinCustom(QMainWindow):
    def __init__(self, titleHeight: int = 20):
        super(MainWinCustom, self).__init__()

        # 鼠标拖拽缩放窗口的识别区域宽度
        self._padding = 20
        # 设置标题栏的高度，作为双击标题栏的识别区域范围
        self.titleHeight = titleHeight
        # 隐藏系统默认框架
        self.setWindowFlags(Qt.FramelessWindowHint)
        # 一些鼠标状态
        self.is_mousePressed = False
        self.is_resizing = False
        self.resize_direction = 'right'
        # 一些功能启用状态
        self.support_resize = True
        self.support_double_max = True

        self.timer_cursorUpdater = QTimer(self)

    def set_mouse_tracking(self):
        self.ui.frame.setMouseTracking(True)
        self.ui.centralwidget.setMouseTracking(True)
        self.setMouseTracking(True)  # / 设置widget鼠标跟踪

    def disable_window_resize(self):
        self.support_resize = False
        self.stop_cursorUpdater()

    def disable_double_max(self):
        self.support_double_max = False

    def set_titleHeight(self, height: int):
        self.titleHeight = height

    def set_frame_style(self, frame: QFrame):
        frame.setFrameStyle(QFrame.Panel)

    def set_comboBoxView(self):
        comboBoxes = self.findChildren(QComboBox)
        for comboBox in comboBoxes:
            comboBox.setView(QListView())

    def set_max_button(self, btn: QToolButton or QPushButton):
        btn.clicked.connect(self.change_window_size)

    def set_min_button(self, btn: QToolButton or QPushButton):
        btn.clicked.connect(self.showMinimized)

    def set_close_button(self, btn: QToolButton or QPushButton):
        btn.clicked.connect(self.close)

    '''
        以下重写窗口拖拽事件
    '''

    def resizeEvent(self, QResizeEvent):
        # 获取有效识别区域
        self.right_l = self.width() - self._padding
        self.right_r = self.width() + 1
        self.bottom_u = self.height() - self._padding
        self.bottom_d = self.height() + 1
        # # 识别区域
        # print('x识别区间：', self.right_l, ',', self.right_r)
        # print('y识别区间：', self.bottom_u, ',', self.bottom_d)

    def get_mouse_pos_of_window(self) -> QPoint:
        global_pos = QCursor.pos()
        window_pos = self.mapFromGlobal(global_pos)
        return window_pos

    def get_mouse_global_pos(self) -> QPoint:
        return QCursor.pos()

    def check_pos_status(self):
        '''
        获取鼠标相对于窗口的坐标，根据鼠标所处的位置返回鼠标的状态
        '''
        pos: QPoint = self.get_mouse_pos_of_window()
        if pos.x() >= self.right_l and pos.x() < self.right_r:
            if pos.y() >= 0:
                if pos.y() <= self.bottom_u:
                    return pos, "right"  # 右边界
                elif pos.y() < self.bottom_d:
                    return pos, "corner"  # 右下角
        if pos.y() >= self.bottom_u and pos.y() < self.bottom_d:
            if pos.x() >= 0:
                if pos.x() <= self.right_l:
                    return pos, 'bottom'  # 下边界
        if pos.x() >= 0 and pos.x() <= self.right_l and pos.y() >= 0 and pos.y() <= self.titleHeight:
            return pos, 'title'
        return pos, False

    def start_cursorUpdater(self):
        '''
        启动计时器更新鼠标样式
        '''
        if self.timer_cursorUpdater:
            self.timer_cursorUpdater.timeout.connect(self.update_cursor)
            self.timer_cursorUpdater.start(100)  # 每隔1秒触发一次鼠标移动事件

    def stop_cursorUpdater(self):
        '''
        停止计时器更新鼠标样式
        '''
        if self.timer_cursorUpdater:
            self.timer_cursorUpdater.stop()

    def update_cursor(self):
        '''
        根据鼠标坐标获取状态，设定鼠标样式
        '''
        pos, status = self.check_pos_status()
        if status is False:
            self.setCursor(Qt.ArrowCursor)
        elif status == 'right':
            self.setCursor(Qt.SizeHorCursor)
        elif status == 'bottom':
            self.setCursor(Qt.SizeVerCursor)
        elif status == 'corner':
            self.setCursor(Qt.SizeFDiagCursor)
        else:
            self.setCursor(Qt.ArrowCursor)

    def change_window_size(self):
        if not self.isMaximized():
            self.showMaximized()
            self.stop_cursorUpdater()
        elif self.isMaximized():
            self.showNormal()
            self.start_cursorUpdater()
        return self.isMaximized()

    def mousePressEvent(self, event: QMouseEvent):
        self.is_mousePressed = True
        self.mouse_pos = self.get_mouse_global_pos()

    def mouseReleaseEvent(self, event: QMouseEvent):
        self.is_mousePressed = False
        self.is_resizing = False

    def mouseDoubleClickEvent(self, *args, **kwargs):
        '''
        检测标题栏的双击状态，切换最大化与正常窗口
        '''
        if self.check_pos_status()[1] == 'title' and self.support_double_max:
            self.change_window_size()

    def mouseMoveEvent(self, event: QMouseEvent):
        if self.is_resizing:
            pos = self.get_mouse_pos_of_window()
            if self.resize_direction == 'right' and self.support_resize:
                self.resize(pos.x(), self.height())
            elif self.resize_direction == 'down' and self.support_resize:
                self.resize(self.width(), pos.y())
            elif self.resize_direction == 'right-down' and self.support_resize:
                self.resize(pos.x(), pos.y())
            elif self.resize_direction == 'move':
                current_pos = self.get_mouse_global_pos()
                self.move(self.pos() + current_pos - self.mouse_pos)
                self.mouse_pos = current_pos
        else:
            if self.is_mousePressed:
                pos, status = self.check_pos_status()
                if status is False:
                    self.is_resizing = False
                if status == 'right':
                    # self.resize(pos.x(), self.height())
                    self.is_resizing = True
                    self.resize_direction = 'right'
                elif status == 'bottom':
                    self.is_resizing = True
                    self.resize_direction = 'down'
                elif status == 'corner':
                    self.is_resizing = True
                    self.resize_direction = 'right-down'
                elif status == 'title':
                    self.is_resizing = True
                    self.resize_direction = 'move'

    def hideEvent(self, *args, **kwargs):
        self.stop_cursorUpdater()

    def showEvent(self, *args, **kwargs):
        if self.support_resize:
            self.start_cursorUpdater()

class MyWin(MainWinCustom):
    def __init__(self):
        super().__init__()

        self.resize(200, 300)

        # 创建一个 QWidget 作为中心区域的容器
        central_widget = QWidget(self)

        # 在中心容器中添加其他控件
        label_title = QLabel("这是标题栏", central_widget)
        label_title.setAlignment(Qt.AlignHCenter | Qt.AlignVCenter)
        label_title.setFixedHeight(40)
        label_title.setStyleSheet("""
        QLabel{
        background: yellow;
        }""")
        layout = QVBoxLayout(central_widget)
        layout.addWidget(label_title)
        label = QLabel('Power by 浅若清风cyf\n\t2023/06/16\n')
        label.setAlignment(Qt.AlignHCenter | Qt.AlignVCenter)
        layout.addWidget(label)
        layout.setContentsMargins(0, 0, 0, 0)

        # 将中心容器设置为 QMainWindow 的中心部件
        self.setCentralWidget(central_widget)
		# 设置label_title的高度为有效识别区域
        self.set_titleHeight(label_title.height())


if __name__ == '__main__':
    QtCore.QCoreApplication.setAttribute(Qt.AA_EnableHighDpiScaling)
    QtGui.QGuiApplication.setAttribute(Qt.HighDpiScaleFactorRoundingPolicy.PassThrough)
    app = QApplication(sys.argv)
    win = MyWin()
    win.show()
    sys.exit(app.exec_())
